<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="./node_modules/d3/dist/d3.min.js"></script>
</head>
<body>

</body>
</html>
<script>
    /* *
    * 初始化数据
    * */
    let nodes = [
            {name: "湖南邵阳"},
            {name: "山东莱州"},
            {name: "广东阳江"},
            {name: "山东枣庄"},
            {name: "泽"},
            {name: "恒"},
            {name: "鑫"},
            {name: "明山"},
            {name: "班长"}
        ],
        edges = [
            {source: 0, target: 4, relation: "籍贯", value: 1.3},
            {source: 4, target: 5, relation: "舍友", value: 1},
            {source: 4, target: 6, relation: "舍友", value: 1},
            {source: 4, target: 7, relation: "舍友", value: 1},
            {source: 1, target: 6, relation: "籍贯", value: 2},
            {source: 2, target: 5, relation: "籍贯", value: 0.9},
            {source: 3, target: 7, relation: "籍贯", value: 1},
            {source: 5, target: 6, relation: "同学", value: 1.6},
            {source: 6, target: 7, relation: "朋友", value: 0.7},
            {source: 6, target: 8, relation: "职责", value: 2}
        ];
    const radius = 10,
        padding = {top: 10, right: 10, bottom: 10, left: 10},
        width = 500,
        height = 500;
    /*
    * 创建svg
    * */
    const body = d3.select('body');
    body.append('svg')
        .attr('width', width)
        .attr('height', height)
        .attr('style', 'background-color:rgba(0,0,0,.1)');
    const svg = body.select('svg');
    const g = svg.append('g')
        .attr('transform', `translate(${padding.left},${padding.top})`);

    /*
    * 创建颜色比例尺
    * */
    const scaleColor =
        d3.scaleOrdinal()
            .domain(d3.range(nodes.length))
            .range(d3.schemeCategory10);

    /*
    * 创建力导向图
    * */
    const forceSimulation =
        d3.forceSimulation()
            .force('link', d3.forceLink())
            .force('change', d3.forceManyBody())
            .force('center', d3.forceCenter());

    /*
    * 生成节点数据
    * */
    const nodesCall = forceSimulation.nodes(nodes);

    /*
    * 生成边界数据
    * */
    forceSimulation.force('link')
        .links(edges)
        .distance(d => {
            return d.value * 100
        });

    /*
    * 设置图形的位置
    * */
    forceSimulation.force('center')
        .x(width / 2)
        .y(height / 2);

    /*
    * 绘制边
    * */
    let links =
        g.append('g')
            .selectAll('line')
            .data(edges)
            .enter()
            .append('line')
            .attr('stroke', (d, i) => {
                return scaleColor(i);
            });
    /*
    * 添加边上的文字
    * */
    let linkTexts =
        g.append('g')
            .selectAll('text')
            .data(edges)
            .enter()
            .append('text')

            .text(d => {
                return d.relation
            });

    /*
    * 创建存放节点和文字的盒子
    * */
    const gs =
        g.selectAll('.textNode')
            .data(nodes)
            .enter()
            .append('g')
            .attr('transform', (d, i) => {
                const cx = d.x,
                    cy = d.y;
                return `translate(${cx},${cy})`
            });

    /*
    * 添加节点
    * */
    gs.append('circle')
        .attr('r', radius)
        .attr('fill', (d, i) => {
            return scaleColor(i)
        });
    /*
    * 添加节点上的文字
    * */
    gs.append('text')
        .attr('x', -10)
        .attr('y', -10)
        .text(d => {
            return d.name
        });
    const ticked = () => {
        links
            .attr('x1', d => {
                return d.source.x
            })
            .attr('y1', d => {
                return d.source.y
            })
            .attr('x2', d => {
                return d.target.x
            })
            .attr('y2', d => {
                return d.target.y
            });
        linkTexts
            .attr('x', d => {
                return (d.source.x + d.target.x) / 2
            })
            .attr('y', d => {
                return (d.source.y + d.target.y) / 2
            });
        gs.attr('transform', d => {
            return `translate(${d.x},${d.y})`
        });
    };
    nodesCall.on('tick', ticked);
    const linked = (d) => {
        if (!d3.event.active) {
            forceSimulation.alphaTarget(0.8).restart()
        }
        d.fx = d.x;
        d.fy = d.y;
    };
    const dragged = (d) => {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    };
    const ended = (d) => {
        if (!d3.event.active) {
            forceSimulation.alphaTarget(0)
        }
        d.fx = null
        d.fy = null
    };
    gs.call(d3.drag()
        .on('start', linked)
        .on('drag', dragged)
        .on('end', ended))


</script>
